package org.eclipse.soc.ide4edu.assignment.operations;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.wizards.datatransfer.ImportOperation;
import org.eclipse.ui.wizards.datatransfer.ZipFileStructureProvider;

public class DownloadOperation {

	/* The temporary save path for assignments */
	final String SAVE_PATH = new String(System.getProperty("java.io.tmpdir"));

	File savedFile;
	List<Object> contents = null;
	String link;
	String username;
	String password;

	HttpClient client;
	PostMethod post;
	GetMethod get;

	URL loginURL;

/**
 * 
 * @param link the download link
 * @param loginURL the login url
 */
	public DownloadOperation(String link, URL loginURL) {

		this.link = link;
		this.loginURL = loginURL;
	}
/**
 * Authenticates a user through eclipse
 * @param username The Username
 * @param password The Password
 * @return true if authentication was successful
 */
	public boolean authenticate(String username, String password) {

		this.username = username;
		this.password = password;

		try {

			client = new HttpClient();
			post = new PostMethod(loginURL.toString());

			post.addParameter("username", username);
			post.addParameter("password", password);

			int statusCode = client.executeMethod(post);

			if (statusCode == -1) {
				showErrorDialog("Connection Error (Status Code -1)");
				return false;
			}

		} catch (Exception e) {
			showErrorDialog(e.getMessage());
		}

		return true;
	}

	/**
	 * Downloads the assignment to the temporary path
	 * @throws IOException
	 */
	public void downloadAssignment() throws IOException {

		get = new GetMethod(link);
		get.setFollowRedirects(true);
		client.executeMethod(get);

		final String tmpPath = SAVE_PATH + "/tmpAssignment.zip";
		IRunnableWithProgress op = new IRunnableWithProgress() {

			byte[] buffer = new byte[1024];

			public void run(final IProgressMonitor monitor) {
				try {
					monitor.beginTask("Downloadin...", 100);
					InputStream zipInStream = get.getResponseBodyAsStream();

					monitor.worked(50);
					FileOutputStream fileOutStream = new FileOutputStream(
							tmpPath);

					int l;
					while ((l = zipInStream.read(buffer)) != -1) {
						fileOutStream.write(buffer, 0, l);
					}
					monitor.worked(50);
					fileOutStream.close();
					zipInStream.close();
					monitor.done();

				} catch (FileNotFoundException e) {
					e.printStackTrace();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}

		};

		try {
			new ProgressMonitorDialog(new Shell()).run(true, false, op);
		} catch (InterruptedException e) {

		} catch (InvocationTargetException e) {
			Throwable realException = e.getTargetException();
			MessageDialog.openError(new Shell(), "Error", realException
					.getMessage());
		}

		post.releaseConnection();
		get.releaseConnection();

		importAssignment(tmpPath);

	}
/**
 * Imports the downloaded assignment in the workspace. Recreates an assignment if the user chooses to overwrite an Assignment.
 * @param tmpPath the Temp save path
 * @throws ZipException
 * @throws IOException
 */
	private void importAssignment(String tmpPath) throws ZipException,
			IOException {
		final ZipFile zipSource = new ZipFile(new File(tmpPath));
		if (zipSource == null)
			return;

		ZipFileStructureProvider structureProvider = new ZipFileStructureProvider(
				zipSource);
		if (structureProvider == null)
			return;

		Object rootFolder = structureProvider.getChildren(
				structureProvider.getRoot()).get(0);
		List files = structureProvider.getChildren(rootFolder);

		Object projectDescFile = null;

		for (Object child : files) {
			final String elementLabel = structureProvider.getLabel(child);

			if (elementLabel.equals(IProjectDescription.DESCRIPTION_FILE_NAME)) {
				projectDescFile = child;
				break;
			}
		}

		InputStream stream = structureProvider.getContents(projectDescFile);

		if (stream == null)
			return;

		final IWorkspace workspace = ResourcesPlugin.getWorkspace();

		IProjectDescription description = null;

		try {
			description = workspace.loadProjectDescription(stream);
			stream.close();
		} catch (CoreException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

		String projectName = description.getName();
		IProjectDescription newDesc = workspace
				.newProjectDescription(projectName);

		IProject project = workspace.getRoot().getProject(projectName);

		try {
			contents = new ArrayList<Object>();
			readFiles(structureProvider, rootFolder);

			if (project.exists() == false)
				project.create(newDesc, null);

			else{
				boolean cont=askOverwrite();
				if(cont==false)
					return;
			}
			//Recreating projects , as we do not want to import the root folder onto the existing assignment
			project.delete(true, null);
			project.create(newDesc,null);
			project.open(null);

			ImportOperation op = new ImportOperation(project.getFullPath(),
					structureProvider.getRoot(), structureProvider,
					new OverwriteStrategy(), contents);

			op.setOverwriteResources(true);
			op.setContext(new Shell());
			op.setCreateContainerStructure(false);
			op.run(null);

		} catch (InvocationTargetException e) {
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (CoreException e) {
			e.printStackTrace();
		}
	}
/**
 * Open dialog for overwrite confirmation
 * @return true if OK was pressed
 */
	private boolean askOverwrite() {
		MessageBox msg=new MessageBox(new Shell());
		msg.setMessage("Assignment Exists! Overwrite?");
		msg.setText("Overwrite?");
		int choice=msg.open();
		if(choice==SWT.OK)
		return true;
		return false;
	}
/**
 * Reads the files and folders recursively and adds them to the list for import.
 * @param structureProvider The instance ZipFileStructureProvider 
 * @param folder The folder Object
 */
	synchronized private void readFiles(
			ZipFileStructureProvider structureProvider, Object folder) {

		List files = structureProvider.getChildren(folder);

		try {
			for (Object child : files) {

				if (((ZipEntry) child).isDirectory() == true) {
					contents.add(child);
					readFiles(structureProvider, child);
					continue;
				}
				contents.add(child);

			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
/**
 * Opens a error dialog
 * @param msg The error message
 */
	private void showErrorDialog(String msg) {
		MessageBox msgBox = new MessageBox(new Shell(), SWT.ABORT);
		msgBox.setText("Connection Error");
		msgBox.setMessage(msg);
		msgBox.open();
	}

}
